/**
 * 比较函数类型定义
 * 用于自定义数组元素的比较逻辑
 */
export type CompareFn<T> = (a: T, b: T) => boolean

/**
 * 计算两个数组的交集
 * 支持基本类型和对象类型，可自定义比较逻辑
 * 
 * @template T 数组元素类型
 * @param arr1 第一个数组
 * @param arr2 第二个数组
 * @param compareFn 可选的比较函数，用于自定义对象的比较逻辑
 * @returns 包含两个数组共有元素的新数组
 * @throws {Error} 当输入参数不是数组时抛出错误
 * 
 * @example
 * // 基本类型交集计算
 * getIntersection([1, 2, 3], [2, 3, 4]) // [2, 3]
 * 
 * @example
 * // 字符串类型交集计算
 * getIntersection(['a', 'b', 'c'], ['b', 'c', 'd']) // ['b', 'c']
 * 
 * @example
 * // 对象数组交集计算
 * const arr1 = [{id: 1, name: 'Alice'}, {id: 2, name: 'Bob'}]
 * const arr2 = [{id: 2, name: 'Bob'}, {id: 3, name: 'Charlie'}]
 * getIntersection(arr1, arr2, (a, b) => a.id === b.id) // [{id: 2, name: 'Bob'}]
 * 
 * @example
 * // 空数组处理
 * getIntersection([], [1, 2, 3]) // []
 * getIntersection([1, 2, 3], []) // []
 */
export const getIntersection = <T>(
	arr1: T[], 
	arr2: T[], 
	compareFn?: CompareFn<T>
): T[] => {
	// 参数验证：确保输入参数都是数组
	if (!Array.isArray(arr1) || !Array.isArray(arr2)) {
		throw new Error('输入参数必须是数组')
	}

	// 空数组快速返回：如果任一数组为空，直接返回空数组
	if (arr1.length === 0 || arr2.length === 0) {
		return []
	}

	// 如果没有提供比较函数，并且数组元素是基本类型，使用Set进行优化
	// 这种方式对于大数组有更好的性能表现
	if (!compareFn && arr1.every(item => 
		typeof item === 'string' || 
		typeof item === 'number' || 
		typeof item === 'symbol'
	)) {
		// 选择较小的数组创建Set，以优化空间和时间复杂度
		const [smallerArr, biggerArr] = arr1.length < arr2.length 
			? [arr1, arr2] 
			: [arr2, arr1]

		const set = new Set(smallerArr)
		// 过滤较大数组中存在于Set中的元素
		return biggerArr.filter(item => set.has(item))
	}

	// 对象比较或自定义比较逻辑
	// 默认比较函数：对于对象使用JSON序列化比较，对于基本类型使用严格相等比较
	const defaultCompareFn: CompareFn<T> = (a, b) => {
		if (typeof a === 'object' && a !== null) {
			// 对象比较：使用JSON序列化进行深度比较
			return JSON.stringify(a) === JSON.stringify(b)
		}
		// 基本类型比较：使用严格相等运算符
		return a === b
	}

	// 使用提供的比较函数或默认比较函数
	const compareFunction = compareFn || defaultCompareFn
	
	// 性能优化：选择较小的数组作为基准来减少比较次数
	const [smallerArr, biggerArr] = arr1.length < arr2.length 
		? [arr1, arr2] 
		: [arr2, arr1]

	// 过滤较小数组中的元素，保留同时存在于较大数组中的元素
	return smallerArr.filter(item1 => 
		biggerArr.some(item2 => compareFunction(item1, item2))
	)
}